From 2539828eac69fd9aa7f8a9b04fa2e0bfe94dd9d6 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 16 Oct 2019 14:57:39 +0200 Subject: [PATCH] listview: Add a focus tracker This ensures that the row with the input focus always stays available, even when scrolled out of view. --- gtk/gtklistview.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index 6eb896e79c..90ae09854a 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -73,6 +73,8 @@ struct _GtkListView double anchor_align; /* the last item that was selected - basically the location to extend selections from */ GtkListItemTracker *selected; + /* the item that has input focus */ + GtkListItemTracker *focus; }; struct _ListRow @@ -662,9 +664,25 @@ gtk_list_view_focus (GtkWidget *widget, old_focus_child = gtk_widget_get_focus_child (widget); + if (old_focus_child == NULL && + (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD)) + { + ListRow *row; + guint pos; + + /* When tabbing into the listview, don't focus the first/last item, + * but keep the previously focused item + */ + pos = gtk_list_item_tracker_get_position (self->item_manager, self->focus); + row = gtk_list_item_manager_get_nth (self->item_manager, pos, NULL); + if (row && gtk_widget_grab_focus (row->parent.widget)) + goto moved_focus; + } + if (!GTK_WIDGET_CLASS (gtk_list_view_parent_class)->focus (widget, direction)) return FALSE; +moved_focus: new_focus_child = gtk_widget_get_focus_child (widget); if (old_focus_child != new_focus_child && @@ -730,6 +748,11 @@ gtk_list_view_dispose (GObject *object) gtk_list_item_tracker_free (self->item_manager, self->selected); self->selected = NULL; } + if (self->focus) + { + gtk_list_item_tracker_free (self->item_manager, self->focus); + self->focus = NULL; + } g_clear_object (&self->item_manager); G_OBJECT_CLASS (gtk_list_view_parent_class)->dispose (object); @@ -931,6 +954,27 @@ gtk_list_view_unselect_all (GtkWidget *widget, gtk_selection_model_unselect_all (selection_model); } +static void +gtk_list_view_update_focus_tracker (GtkListView *self) +{ + GtkWidget *focus_child; + guint pos; + + focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self)); + if (!GTK_IS_LIST_ITEM (focus_child)) + return; + + pos = gtk_list_item_get_position (GTK_LIST_ITEM (focus_child)); + if (pos != gtk_list_item_tracker_get_position (self->item_manager, self->focus)) + { + gtk_list_item_tracker_set_position (self->item_manager, + self->focus, + pos, + GTK_LIST_VIEW_EXTRA_ITEMS, + GTK_LIST_VIEW_EXTRA_ITEMS); + } +} + static void gtk_list_view_scroll_to_item (GtkWidget *widget, const char *action_name, @@ -975,6 +1019,14 @@ gtk_list_view_scroll_to_item (GtkWidget *widget, else gtk_list_view_set_anchor (self, pos, 1.0); } + + /* HACK HACK HACK + * + * GTK has no way to track the focused child. But we now that when a listitem + * gets focus, it calls this action. So we update our focus tracker from here + * because it's the closest we can get to accurate tracking. + */ + gtk_list_view_update_focus_tracker (self); } static void @@ -1184,6 +1236,7 @@ static void gtk_list_view_init (GtkListView *self) { self->item_manager = gtk_list_item_manager_new (GTK_WIDGET (self), "row", ListRow, ListRowAugment, list_row_augment); + self->focus = gtk_list_item_tracker_new (self->item_manager); self->anchor = gtk_list_item_tracker_new (self->item_manager); self->selected = gtk_list_item_tracker_new (self->item_manager); self->orientation = GTK_ORIENTATION_VERTICAL; -- 2.30.2